package cn.cs.message.tempstorage.disk;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.util.Iterator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.cs.message.Message;

public class DiskTempStorageIterator implements Iterator<Message>
{
	private static final Logger logger=LoggerFactory.getLogger(DiskTempStorageIterator.class);
	private static final int CHUNK_SIZE = 4096;
	private Iterator<File> files;
	
	private Message rmsg;
	public long getCount() {
		return count;
	}

	private long position=0;
	private FileChannel channel;
	private MappedByteBuffer buffer;
	private RandomAccessFile randomAccessFile;
	private long count=0;

	@Override
	public boolean hasNext() 
	{
		MsgDecoder decoder;
		int size;
		do{
			buffer = getBuffer(MsgDecoder.HEAD_SIZE);
			if(buffer==null)
				return false;
			
			decoder=new MsgDecoder(buffer);
			position+=MsgDecoder.HEAD_SIZE;
			size=decoder.size();
			if(size==0)
			{
				buffer=nextFileBuffer(size);
				continue;
			}
			buffer = getBuffer(size);
			rmsg=decoder.decode(buffer);
			count++;
			position+=size;
			if(rmsg!=null)
				return true;
		}while(true);	
	}
	
	private MappedByteBuffer nextFileBuffer(long length)
	{
		if (channel != null) 
		{
			try {
				buffer=null;
				channel.close();
				randomAccessFile.close();
			} catch (IOException e) {
				logger.error("IOException",e);
			}
			channel = null;
		}
		if (files.hasNext()) 
		{
			try{
				File file = files.next();
				randomAccessFile=new RandomAccessFile(file, "r");
				channel = randomAccessFile.getChannel();
				position = 0;
				long fileSize=channel.size();
				fileSize=fileSize<length?fileSize:length;
				return buffer=channel.map(MapMode.READ_ONLY, position, fileSize>CHUNK_SIZE?fileSize:CHUNK_SIZE);
			}catch (IOException e) {
				logger.error("IOException",e);
			}
		} 
		return buffer=null;
	}
	
	private MappedByteBuffer getBuffer(long length) 
	{
		if (!(buffer == null ||buffer.remaining()<=length||!buffer.hasRemaining()))
			return buffer;

		try{
			if (channel == null || channel.size() == position) 
			{
				if (channel != null) 
				{
					try {
						buffer=null;
						channel.close();
						randomAccessFile.close();
					} catch (IOException e) {
						logger.error("IOException",e);
					}
					channel = null;
				}
				if (files.hasNext()) 
				{
					File file = files.next();
					randomAccessFile=new RandomAccessFile(file, "r");
					channel = randomAccessFile.getChannel();
					position = 0;
				} else 
					return buffer=null;
			}
			long fileSize=channel.size(),remain=fileSize-position;
			if (remain<length) {
				length =(int)(fileSize-position);
				return nextFileBuffer(length);
			}
			else 
				return buffer=channel.map(MapMode.READ_ONLY, position, length);
		}catch (IOException e) {
			logger.error("IOException",e);
			return null;
		}
	}

	@Override
	public Message next() 
	{
		Message res=rmsg;
		rmsg=null;
		return res;
	}
	
	public DiskTempStorageIterator(Iterator<File> files)
	{
		count=0;
		this.files=files;
	}
	
	public Iterator<File> getFiles() {
		return files;
	}

	public void setFiles(Iterator<File> files) {
		count=0;
		this.files = files;
	}

	@Override
	public void remove() 
	{
		throw new UnsupportedOperationException();
	}

}
